package com.example.estemplate.service;

import com.example.estemplate.dto.*;
import com.example.estemplate.enums.TemplateOperateState;
import com.example.estemplate.exception.TemplateSyncException;
import com.example.estemplate.processor.TemplateManagerProcessor;
import org.elasticsearch.client.IndicesClient;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexTemplatesRequest;
import org.elasticsearch.client.indices.GetIndexTemplatesResponse;
import org.elasticsearch.client.indices.IndexTemplateMetadata;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.example.estemplate.dto.TemplatesOperateResult;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;

@Slf4j
@Service
public class TemplateManagerService {

    private final RestHighLevelClient esClient;
    private final ObjectMapper objectMapper;
    private final Executor taskExecutor;
    private final TemplateManagerProcessor templateManagerProcessor;


    @Value("${template.base-path}")
    private String templateBasePath;

    @Value("${template.file-pattern}")
    private String templateFilePattern;

    public TemplateManagerService(RestHighLevelClient esClient,
                                  ObjectMapper objectMapper,
                                  @Qualifier("taskExecutor") Executor taskExecutor,
                                  TemplateManagerProcessor templateManagerProcessor
    ) {
        this.esClient = esClient;
        this.objectMapper = objectMapper;
        this.taskExecutor = taskExecutor;
        this.templateManagerProcessor = templateManagerProcessor;
    }

    /**
     * 导入项目中的所有模板文件，多线程更新对应的索引模板（带降级策略）
     */
    public TemplatesOperateResult concurrentUpdateAllTemplatesByFile() {
        //key为模板名，value为模板内容(json数据)。
        Map<String, String> templates;
        try {
            //加载所有模板文件内容
            templates = loadAllTemplateFiles();
        } catch (Exception e) {
            log.error("loadFileConcurrentUpdateAllTemplate,templates load error", e);
            throw new TemplateSyncException("Template load failed", e);
        }

        // 执行并发更新（带降级策略）
        return concurrentUpdateWithFallback(templates);
    }

    /**
     * 导入指定的模板对应的模板文件，多线程更新对应的索引模板（带降级策略）
     * @param templateNames 要更新的模板名称列表
     * @return 模板操作结果
     */
    public TemplatesOperateResult concurrentUpdateTemplatesByFile(List<String> templateNames) {
        //key为模板名，value为模板内容(json数据)。
        Map<String, String> templates;
        try {
            //加载模板内容
            templates = loadSpecifiedTemplateFiles(templateNames);
        } catch (Exception e) {
            log.error("loadFileConcurrentUpdateTemplate,templates load error,templateNames:{}", templateNames, e);
            throw new TemplateSyncException("Template load failed", e);
        }

        // 执行并发更新（带降级策略）
        return concurrentUpdateWithFallback(templates);
    }

    /**
     * 多线程更新索引模板（带降级策略）
     * @param templates 一个map，key为模板名，value为模板内容(json数据)。
     * @return 合并后的操作结果
     */
    private TemplatesOperateResult concurrentUpdateWithFallback(Map<String, String> templates) {
        try {
            //先尝试并发更新
            TemplatesOperateResult concurrentResult = concurrentUpdateTemplates(templates);

            //如果有失败的，降级为顺序执行
            if (!concurrentResult.getFailTemplates().isEmpty()) {
                log.warn("Concurrent update has {} failures, fallback to sequential",
                        concurrentResult.getFailTemplates().size());

                // 按顺序执行的方式更新
                TemplatesOperateResult sequentialResult = sequentialUpdateTemplates(
                        concurrentResult.getFailTemplates());

                // 合并并发和顺序执行的结果
                return mergeResults(concurrentResult, sequentialResult);
            }

            return concurrentResult;
        } catch (Exception e) {
            throw new TemplateSyncException("Template update failed", e);
        }
    }

    /**
     * 多线程更新索引模板（带重试、降级策略），重试是为了防止网络波动导致的假性失败
     *
     * @param templates 一个map，key为模板名，value为模板内容(json数据)。
     * @return 合并后的操作结果
     */
    private TemplatesOperateResult concurrentUpdateWithRetryFallback(Map<String, String> templates) {
        try {
            //先尝试并发更新
            TemplatesOperateResult concurrentResult = concurrentUpdateTemplates(templates);

            //如果有失败的，降级为顺序执行
            if (!concurrentResult.getFailTemplates().isEmpty()) {
                log.warn("Concurrent update has {} failures, attempting retry",
                        concurrentResult.getFailTemplates().size());

                // 第一次重试(按顺序执行的方式更新)
                TemplatesOperateResult retryResult = sequentialUpdateTemplates(
                        concurrentResult.getFailTemplates());
                // 检查仍有失败的模板
                List<String> stillFailing = new ArrayList<>(retryResult.getFailTemplates());
                if (!stillFailing.isEmpty()) {
                    log.error("{} templates still failing after retry, final fallback",
                            stillFailing.size());

                    // 最终降级处理
                    TemplatesOperateResult finalResult = sequentialUpdateTemplates(stillFailing);
                    // 合并并发和顺序执行的结果
                    return mergeResults(
                            mergeResults(concurrentResult, retryResult),
                            finalResult
                    );
                }

                return mergeResults(concurrentResult, retryResult);
            }

            return concurrentResult;
        } catch (Exception e) {
            throw new TemplateSyncException("Template update failed", e);
        }
    }

    /**
     * 并发更新模板
     * @param templates 一个map，key为模板名，value为模板内容(json数据)。
     * @return 操作结果
     */
    private TemplatesOperateResult concurrentUpdateTemplates(Map<String, String> templates) {
        try {
            // 创建异步任务列表
            List<CompletableFuture<TemplateUpdateResult>> futures =
                    templates.entrySet().stream()
                            .map(entry -> createOrUpdateTemplateByNewApiAsync(entry.getKey(), entry.getValue()))
                            .collect(Collectors.toList());

            // 等待所有异步任务完成
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
                    .get(30, TimeUnit.SECONDS); // 设置超时时间

            // 处理异步任务结果
            return processAsyncResults(futures);
        } catch (Exception e) {
            log.error("Concurrent update failed", e);
            // 返回失败结果
            return new TemplatesOperateResult(
                    new ArrayList<>(),
                    new ArrayList<>(templates.keySet()),
                    new ArrayList<>());
        }
    }

    /**
     * 顺序更新模板（降级策略）
     *
     * @deprecated 已废弃，因为此方法中使用了已弃用的ES方法{@link IndicesClient#putTemplate(PutIndexTemplateRequest, RequestOptions)}，请改用{@link TemplateManagerService#sequentialUpdateTemplates(List)}方法。
     */
    @Deprecated
    private TemplatesOperateResult sequentialUpdate(List<String> templateNames) {
        Map<String, String> templates;
        try {
            templates = loadSpecifiedTemplateFiles(templateNames);
        } catch (IOException e) {
            throw new TemplateSyncException("Failed to load templates", e);
        }

        List<String> success = new ArrayList<>();
        List<String> fail = new ArrayList<>();

        templates.forEach((name, content) -> {
            try {
                PutIndexTemplateRequest request = new PutIndexTemplateRequest(name)
                        .source(content, XContentType.JSON);
                esClient.indices().putTemplate(request, RequestOptions.DEFAULT);
                success.add(name);
            } catch (IOException e) {
                log.error("Sequential update failed for template: {}", name, e);
                fail.add(name);
            }
        });

        return new TemplatesOperateResult(success, fail, new ArrayList<>());
    }

    /**
     * 异步更新单个模板
     *
     * @deprecated 已废弃，因为此方法中使用了已弃用的ES方法{@link IndicesClient#putTemplate(PutIndexTemplateRequest, RequestOptions)}，请改用{@link TemplateManagerService#createOrUpdateTemplateByNewApiAsync(String, String)}方法
     */
    @Deprecated
    public CompletableFuture<TemplateUpdateResult> updateTemplateAsync(
            String templateName, String content) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                PutIndexTemplateRequest request = new PutIndexTemplateRequest(templateName)
                        .source(content, XContentType.JSON);
                esClient.indices().putTemplate(request, RequestOptions.DEFAULT);
                return new TemplateUpdateResult(templateName, TemplateOperateState.SUCCESS);
            } catch (IOException e) {
                log.error("Async update failed for template: {}", templateName, e);
                return new TemplateUpdateResult(templateName, TemplateOperateState.FAIL);
            }
        }, taskExecutor);
    }


    /**
     * 异步更新单个模板
     * @param templateName 模板名称
     * @param content 模板内容
     * @return CompletableFuture包装的更新结果
     */
    public CompletableFuture<TemplateUpdateResult> createOrUpdateTemplateByNewApiAsync(
            String templateName, String content) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                TemplateOperateState state = templateManagerProcessor.createOrUpdateTemplate(templateName, content);
                return new TemplateUpdateResult(templateName, state);
            } catch (Exception e) {
                log.error("Async update failed for template: {}", templateName, e);
                return new TemplateUpdateResult(templateName, TemplateOperateState.FAIL);
            }
        }, taskExecutor);
    }

    /**
     * 处理异步结果
     * @param futures 异步任务列表
     * @return 操作结果
     */
    private TemplatesOperateResult processAsyncResults(
            List<CompletableFuture<TemplateUpdateResult>> futures) {
        //“操作成功”的索引模板
        List<String> success = new ArrayList<>();
        //“操作失败”的索引模板
        List<String> fail = new ArrayList<>();
        //“无需操作”的索引模板（内容无变化的索引模板）
        List<String> noNeed = new ArrayList<>();

        // 遍历所有异步任务结果
        futures.forEach(f -> {
            try {
                TemplateUpdateResult result = f.get();

                // 根据操作状态分类
                switch (result.getState()) {
                    case SUCCESS:
                        success.add(result.getTemplateName());
                        break;
                    case FAIL:
                        fail.add(result.getTemplateName());
                        break;
                    case NO_NEED:
                        noNeed.add(result.getTemplateName());
                        break;
                    default:
                        log.error("processAsyncResults,result error,templateName:{},state:{}", result.getTemplateName(), result.getState());
                }
            } catch (Exception e) {
                log.error("processAsyncResults,Error getting async result", e);
            }
        });

        return new TemplatesOperateResult(success, fail, noNeed);
    }

    /**
     * 合并并发和顺序执行的结果
     * @param concurrentResult 并发执行结果
     * @param sequentialResult 顺序执行结果
     * @return 合并后的结果
     */
    private TemplatesOperateResult mergeResults(
            TemplatesOperateResult concurrentResult,
            TemplatesOperateResult sequentialResult) {
        //所有“操作成功”的索引模板
        List<String> allSuccess = new ArrayList<>(concurrentResult.getSuccessTemplates());
        allSuccess.addAll(sequentialResult.getSuccessTemplates());

        //所有“操作失败”的索引模板
        List<String> allFail = new ArrayList<>(sequentialResult.getFailTemplates());

        //所有“无需操作”的索引模板（内容无变化的索引模板）
        List<String> allNoNeedOperate = new ArrayList<>(concurrentResult.getNoNeedOperateTemplates());
        allNoNeedOperate.addAll(sequentialResult.getNoNeedOperateTemplates());

        return new TemplatesOperateResult(allSuccess, allFail, allNoNeedOperate);
    }

    /**
     * 同步所有模板到ES
     */
    public TemplatesOperateResult syncAllTemplates() {
        //key为模板名，value为模板内容(json数据)。
        Map<String, String> templateFiles;
        try {
            templateFiles = loadAllTemplateFiles();
        } catch (Exception e) {
            log.error("syncAllTemplates,Failed to load template files", e);
            throw new TemplateSyncException("Failed to load template files", e);
        }

        //创建或更新这些索引模板
        return createOrUpdateTemplates(templateFiles);
    }

    /**
     * 使用单线程更新指定的模板到ES
     */
    public TemplatesOperateResult sequentialUpdateTemplates(List<String> templateNames) {
        //key为模板名，value为模板内容(json数据)。
        Map<String, String> templateFiles;
        try {
            templateFiles = loadSpecifiedTemplateFiles(templateNames);
        } catch (Exception e) {
            log.error("syncSpecifiedTemplates,Failed to load template files", e);
            throw new TemplateSyncException("Failed to load template files", e);
        }

        //创建或更新这些索引模板
        return createOrUpdateTemplates(templateFiles);
    }

    /**
     * 根据传入的模板名和模板内容，创建或更新这些模板的内容
     *
     * @param templateFiles 一个map，key为模板名，value为模板内容。
     * @return 模板操作结果
     */
    private TemplatesOperateResult createOrUpdateTemplates(Map<String, String> templateFiles) {
        Map<TemplateOperateState, List<String>> partitionedResults = templateFiles.entrySet().stream()
                .collect(Collectors.groupingBy(
                        //创建或更新单个模板
                        entry -> templateManagerProcessor.createOrUpdateTemplate(entry.getKey(), entry.getValue()),
                        Collectors.mapping(Map.Entry::getKey, Collectors.toList())
                ));

        //“操作成功”的模板
        List<String> successTemplates = partitionedResults.getOrDefault(TemplateOperateState.SUCCESS, Collections.emptyList());
        //“操作失败”的模板
        List<String> failTemplates = partitionedResults.getOrDefault(TemplateOperateState.FAIL, Collections.emptyList());
        //“无需操作”的模板
        List<String> noNeedOperateTemplates = partitionedResults.getOrDefault(TemplateOperateState.NO_NEED, Collections.emptyList());

        TemplatesOperateResult result = new TemplatesOperateResult(successTemplates, failTemplates, noNeedOperateTemplates);
        log.info("createOrUpdateTemplates end, success:{}, fail:{},noNeedOperate(up To Date):{}", successTemplates, failTemplates, noNeedOperateTemplates);
        return result;
    }

    /**
     * 从指定路径加载所有模板文件
     *
     * @return 一个map，key为模板名，value为模板内容(json数据)。
     */
    public Map<String, String> loadAllTemplateFiles() throws IOException {
        // 创建资源解析器
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // 构建资源路径模式
        String locationPattern = String.format("%s/%s", templateBasePath, templateFilePattern);
        // 获取所有匹配的资源
        Resource[] resources = resolver.getResources(locationPattern);

        Map<String, String> templates = new LinkedHashMap<>();

        // 遍历所有资源文件
        for (Resource resource : resources) {
            String filename = resource.getFilename();
            // 检查是否为模板文件
            if (filename != null && filename.endsWith("_template.json")) {
                // 提取模板名称
                String templateName = filename.replace(".json", "");
                // 读取文件内容
                String content = StreamUtils.copyToString(resource.getInputStream(), StandardCharsets.UTF_8);
                templates.put(templateName, content);
                log.debug("Loaded template: {}", templateName);
            }
        }

        return templates;
    }

    /**
     * 导入指定的es索引模板文件，使用ClassPathResource
     *
     * @param templateNames 模板名列表
     * @return 一个map，key为模板名，value为模板内容(json数据)。
     */
    public Map<String, String> loadSpecifiedTemplateFilesByCPR(List<String> templateNames) throws IOException {
        Map<String, String> templates = new LinkedHashMap<>();
        for (String templateName : templateNames) {
            ClassPathResource resource = new ClassPathResource("es-templates/" + templateName + ".json");
            String content = FileCopyUtils.copyToString(
                    new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8)
            );

            templates.put(templateName, content);
        }

        return templates;
    }

    /**
     * 导入指定的es索引模板文件
     *
     * @param templateNames 模板名列表
     * @return 一个map，key为模板名，value为模板内容(json数据)。
     */
    public Map<String, String> loadSpecifiedTemplateFiles(List<String> templateNames) throws IOException {
        Map<String, String> templates = new LinkedHashMap<>();
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

        for (String templateName : templateNames) {
            String resourcePath = templateBasePath + "/" + templateName + ".json";
            Resource resource = resolver.getResource(resourcePath);

            if (!resource.exists()) {
                log.error("Template file not found: {}", resourcePath);
                continue;
            }

            String content = StreamUtils.copyToString(
                    resource.getInputStream(), StandardCharsets.UTF_8);
            templates.put(templateName, content);
        }

        return templates;
    }

    /**
     * 获取 src/main/resources/es-templates 目录下所有的索引模板文件的对应的模板名称
     */
    public List<String> getAllFileTemplateNames() throws IOException {
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String locationPattern = templateBasePath + "/" + templateFilePattern;

        Resource[] resources = resolver.getResources(locationPattern);

        return Arrays.stream(resources)
                .map(Resource::getFilename)
                .filter(StringUtils::hasText)
                .map(o -> o.replaceAll(".json", ""))
                .collect(Collectors.toList());
    }


    /**
     * 获取ES服务端上的所有模板名称
     * @return 模板名称列表
     * @throws IOException ES操作异常
     */
    public List<String> getAllTemplateNamesInEs() throws IOException {
        // 创建获取模板请求（匹配所有模板）
        GetIndexTemplatesRequest request = new GetIndexTemplatesRequest("*");
        // 执行请求获取响应
        GetIndexTemplatesResponse response = esClient.indices().getIndexTemplate(request, RequestOptions.DEFAULT);
        // 提取并返回模板名称列表
        return response.getIndexTemplates().stream()
                .map(IndexTemplateMetadata::name)
                .collect(Collectors.toList());
    }
}